import sys
import mod_direction,mod_game,mod_gui
from priodict import priorityDictionary
import fov
import random
import mod_line,mod_item,mod_player
from mod_entity import EntityPrototype,Entity
import mod_vector

class ActorPrototype(EntityPrototype):
    def __init__(self,glyph,maxHP,maxAP,APperTurn,brainPrototype,startingItemPrototypes=None,structural=False):
        EntityPrototype.__init__(self,glyph,maxHP,startingItemPrototypes,structural=structural)
        self.maxAP = maxAP
        self.APperTurn = APperTurn
        self.abilities = []
        self.brainPrototype = brainPrototype
        
    def instantiateBrain(self,actor):
        return self.brainPrototype(actor)
        
    def instantiate(self):
        newActor = Actor(self)
        self.createStartingInventory(newActor)
        return newActor

class Actor(Entity):
    
    def __init__(self,prototype):
        Entity.__init__(self,prototype)
        self.brain = prototype.instantiateBrain(self)
        self.AP = 0
        self.neighbors = []
        self.eqMap = {}
    
    def generateNeighbors(self):
        self.neighbors = []
        if self.location:
            coords = self.location[1]
            for dir in mod_direction.ALLDIRS:
                self.neighbors.append((coords[0]+dir.xOffset,coords[1]+dir.yOffset))
    
    def takeTurn(self):
        level,coords = self.location
        itemsHere = level.itemMap.get(coords)
        if itemsHere:
            for item in itemsHere:
                startTurnOnEffect = item.startTurnOnEffect
                if startTurnOnEffect:
                    startTurnOnEffect.doIt(self,item)
        self.setAP(self.AP+self.APperTurn)
        if self.brain:
            self.brain.takeTurn()

    def setAP(self,newAP):
        if newAP < 0:
            raise NegativeAPError,'Negative AP!'
        if newAP > self.maxAP:
            newAP = self.maxAP
        self.AP = newAP
    
    def useAP(self,AP):
        self.setAP(self.AP-AP)
    
    def setLocation(self,newLocation):
        if self.location:
            oldLevel = self.location[0]
        else:
            oldLevel = None
        if newLocation:
            newLevel = newLocation[0]
        else:
            newLevel = None
        Entity.setLocation(self,newLocation)
        if oldLevel != newLevel: # If the level has changed...
            if oldLevel:
                oldLevel.removeActor(self)
            if newLevel:
                newLevel.addActor(self)
            self.brain.updateVisibility(oldLevel)
        else:
            self.brain.updateVisibility()
    
    def die(self):
        Entity.die(self)
        mod_gui.theGUI.messages.append(self.glyph+' DIES.')
        self.brain.onActorDeath()
            
    def findItem(self,itemID):
        # We could narrow down the location by using ghost data.
        for item in self.items:
            if item.ID == itemID:
                return item
        if self.location:
            itemsHere = self.location[0].itemMap.get(self.location[1])
            if itemsHere:
                for item in itemsHere:
                    if item.ID == itemID:
                        return item
        # Item not found.
        return None
    
    def equipItem(self,item):
        if item.eqLoc == None:
            raise Exception, "Attempt to equip an item with no eqLoc"
        elif item.equipped:
            raise Exception, "Attempting to equip already-equpped item."
        elif item.eqLoc in self.eqMap:
            raise Exception, "Attempting to equip when slot is already taken."
        else: # Success
            self.eqMap[item.eqLoc] = item
            item.equipped = True
    
    def unequipItem(self,item):
        if not item.equipped:
            raise Exception, "Attempting to equip non-equipped item."
        elif self.eqMap[item.eqLoc] != item:
            raise Exception, "Attempting to equip item in wrong spot."
        else: # Success
            del self.eqMap[item.eqLoc]
            item.equipped = False
            
    def takeDamage(self,damage,type):
        floatDamage = float(damage)
        ratioCount = floatDamage
        for item in self.eqMap.itervalues():
            ratio = item.damageTakeMap.get(type,0.0)
            damageToItem = floatDamage * ratio
            ratioCount -= ratio
            #item.takeDamage()
        if ratioCount < 0:
            ratioCount = 0
            raise Exception, "Damage count less than zero... time to fix this!"
        finalDamage = int(round(ratioCount * floatDamage))
        Entity.takeDamage(self,finalDamage,type)


class NegativeAPError(Exception):
    def __init__(self,value):
        self.value = value
    def __str__(self):
        return repr(self.value)

# These events are used to animate. They should be moved elsewhere.
class MoveEvent:
    def __init__(self,actor,oldLocation,newLocation):
    	self.oldLocation = oldLocation
    	self.newLocation = newLocation
    	self.actor = actor

class ProjectileEvent:
    def __init__(self,originCoords,line,path,projectileGlyph,rotateImage=False):
        self.projectileGlyph = projectileGlyph
    	self.originCoords = originCoords
        self.line = line
        self.path = path
        self.rotateImage = rotateImage
